from copy import deepcopy


def _collect_attrs(ns, attrs, prefix= None):
    if prefix is None:
        prefix = ""
    else:
        prefix += "."
    for k, v in ns:
        if isinstance(v, Config):
            _collect_attrs(v, attrs, prefix + k)
        else:
            attrs.append((prefix + k, v))


class Config(object):
    """
    Nested namespace object: a light-weight, easy to work with, pretty-printing 
    container used to hold nested attributes. Mostly useful to hold configuration
    data, as shown below.
    
    Example:
    
    >>> params = Config()
    >>> params.linker.toolchain = "gcc"
    >>> params.linker.mode = "so"
    >>> params.c.defines.PLAT_ARM9 = True
    >>> params.c.defines.KERNEL = False
    >>> params.c.defines.LOGLEVEL = 6
    >>> params.c.defines.DAY_OF_WEEK = "tuesday"
    >>> params.c.include_dirs = ["/opt/fuck/inc"]
    >>>
    >>> params2 = deepcopy(params)
    >>> params2.c.include_dirs.append("/tmp/xorg/kaki")
    >>>
    >>> print params
    Config:
        c.defines.DAY_OF_WEEK = 'tuesday'
        c.defines.KERNEL = False
        c.defines.LOGLEVEL = 6
        c.defines.PLAT_ARM9 = True
        c.include_dirs = ['/opt/fuck/inc']
        linker.mode = 'so'
        linker.toolchain = 'gcc'
    >>>
    >>> print params.c
    Config:
        defines.DAY_OF_WEEK = 'tuesday'
        defines.KERNEL = False
        defines.LOGLEVEL = 6
        defines.PLAT_ARM9 = True
        include_dirs = ['/opt/fuck/inc']
    >>>
    >>> print params.c.defines
    Config:
        DAY_OF_WEEK = 'tuesday'
        KERNEL = False
        LOGLEVEL = 6
        PLAT_ARM9 = True
    >>>
    >>> print params2
    Config:
        c.defines.DAY_OF_WEEK = 'tuesday'
        c.defines.KERNEL = False
        c.defines.LOGLEVEL = 6
        c.defines.PLAT_ARM9 = True
        c.include_dirs = ['/opt/fuck/inc', '/tmp/xorg/kaki']
        linker.mode = 'so'
        linker.toolchain = 'gcc'
    >>>
    >>> args = []
    >>> for k, v in params2.c.defines:
    ...     if v is True:
    ...         args.append("-D%s" % (k,))
    ...     elif v is False:
    ...         args.append("-U%s" % (k,))
    ...     elif type(v) is str:
    ...         args.append('-D%s="%s"' % (k, v))
    ...     else:
    ...         args.append('-D%s=%s' % (k, v))
    ...
    >>> print "gcc -I%s %s" % (" -I".join(params2.c.include_dirs), " ".join(args))
    gcc -I/opt/fuck/inc -I/tmp/xorg/kaki -DLOGLEVEL=6 -UKERNEL -DPLAT_ARM9 -DDAY_OF_WEEK="tuesday"
    >>>
    >>> "linker" in params
    True
    >>> "archiver" in params
    False
    >>>
    >>>
    >>> if params.linker:
    ...     print "hi!"
    ...
    hi!
    >>> if params.archiver:
    ...     print "oops!"
    ...
    >>>
    """
    def __init__(self, source = None):
        if source is not None:
            for k, v in source:
                setattr(self, k, deepcopy(v, memo))
    def __str__(self):
        attrs = []
        _collect_attrs(self, attrs)
        if attrs:
            attrs.sort()
            text = "\n    ".join("%s = %r" % (k, v) for k, v in attrs)
        else:
            text = "(empty)"
        return "Config:\n    %s" % (text,)
    def __copy__(self):
        new = self.__class__()
        for k, v in self:
            setattr(new, k, v)
        return new
    def __deepcopy__(self, memo = None):
        new = self.__class__()
        for k, v in self:
            setattr(new, k, deepcopy(v, memo))
        return new
    def __nonzero__(self):
        return any(self)
    def __iter__(self):
        for k, v in self.__dict__.iteritems():
            if isinstance(v, Config):
                if v:
                    yield k, v
            else:
                yield k, v
    def __contains__(self, name):
        if name not in self.__dict__:
            return False
        v = self.__dict__[name]
        if not isinstance(v, Config):
            return True
        return bool(v)
    def __getattr__(self, name):
        ns = self.__class__()
        setattr(self, name, ns)
        return ns



if __name__ == "__main__":
    import doctest
    doctest.testmod()










